﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Microsoft Corporation" file="RESXTranslator_Form.cs">
//   Copyright © Microsoft Corporation.  All Rights Reserved.  
// This code released under the terms of the 
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
// </copyright>
// <summary>
//   The resx translator main form.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Microsoft.MCSUK.RESXTranslator
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Windows.Forms;

    using Microsoft.MCSUK.RESXTranslator.Properties;

    /// <summary>The resx translator_ form.</summary>
    public partial class ResxTranslatorForm : Form
    {
        /// <summary>
        /// Start up timer, triggering Bing Login after main page is showing.
        /// </summary>
        private Timer startupTimer;

        /// <summary>
        /// Translation process has been requested to abort by user
        /// </summary>
        private bool translationAbortRequested;

        /// <summary>
        /// Text for the status message
        /// </summary>
        private string statusLineText;

        /// <summary>Initializes a new instance of the <see cref="ResxTranslatorForm"/> class.</summary>
        public ResxTranslatorForm()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Gets or sets the Microsoft Translator Client ID
        /// </summary>
        internal string AzureClientID
        {
            get
            {
                return string.Equals(clientIDTextBox.Text, "not yet set", StringComparison.CurrentCultureIgnoreCase) ? null : clientIDTextBox.Text;
            }

            set
            {
                clientIDTextBox.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets the Microsoft Translator Client Secret
        /// </summary>
        internal string AzureClientSecret
        {
            get
            {
                return string.Equals(clientSecretTextBox.Text, "not yet set", StringComparison.CurrentCultureIgnoreCase) ? null : clientSecretTextBox.Text;
            }

            set
            {
                clientSecretTextBox.Text = value;
            }
        }

        /// <summary>
        /// Gets or sets the source Resx file
        /// </summary>
        internal string SourceResxFile
        {
            get
            {
                return txtSourceRESXPath.Text;
            }

            set
            {
                txtSourceRESXPath.Text = value;
                var matchingCulture = TranslatorTools.FindMatchingCultureFromResxFileName(this.cmbBingFromCultureSingleFile.Items.Cast<CultureInfo>(), value);
                this.cmbBingFromCultureSingleFile.SelectedItem = matchingCulture;
            }
        }

        /// <summary>
        /// Gets or sets the search directory root
        /// </summary>
        internal string SourceSearchDirectory { get; set; }

        /// <summary>
        /// Initializes language combo box for target languages: to language and derived languages
        /// </summary>
        /// <param name="toCulture">The culture</param>
        /// <param name="comboBox">The combobox to initialize</param>
        private static void InitTranslateToCultureComboBox(CultureInfo toCulture, ComboBox comboBox)
        {
            comboBox.Items.Clear();
            if (toCulture != null)
            {
                var allCultures = CultureInfo.GetCultures(CultureTypes.AllCultures);
                comboBox.Items.Add(toCulture);
                foreach (var culture in allCultures.Where(culture => culture.Parent.LCID == toCulture.LCID))
                {
                    comboBox.Items.Add(new CultureComboItem(culture));
                }

                comboBox.SelectedIndex = 0;
                comboBox.Enabled = true;
            }
            else
            {
                comboBox.Text = string.Empty;
                comboBox.Enabled = false;
            }
        }

        /// <summary>The add languages to combo box.</summary>
        /// <param name="comboBox">The combo box.</param>
        /// <param name="cultures">The cultures.</param>
        private static void AddLanguagesToComboBox(ComboBox comboBox, IEnumerable<CultureInfo> cultures)
        {
            comboBox.Items.Clear();
            foreach (var culture in cultures)
            {
                comboBox.Items.Add(culture);
            }

            comboBox.DisplayMember = "DisplayName";
        }

        /// <summary>The get bing languages for translation.</summary>
        /// <returns>Array of languages for translation</returns>
        private IEnumerable<string> GetBingLanguagesForTranslation()
        {
            IEnumerable<string> languagesForTranslate = null;
            try
            {
                this.SetStatusText(Resources.StatusText_LoadingBingLanguages);
                try
                {
                    var authenticationToken = TranslatorTools.RequestAuthenticationToken(
                        this.AzureClientID,
                        this.AzureClientSecret, 
                        this.AddLogText);
                    if (!string.IsNullOrEmpty(authenticationToken))
                    {
                        languagesForTranslate = TranslatorTools.GetLanguagesForTranslate(authenticationToken, this.AddLogText);
                    }
                }
                finally
                {
                    this.ClearStatusText();
                }
            }
            catch
            {
                var log = Resources.ErrorReadingTranslatorLanguageCodes;
                this.AddLogTextLine(log);
                TranslatorTools.LogStatus(
                    log,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
                languagesForTranslate = new List<string>();
            }
            
            return languagesForTranslate;
        }

        /// <summary>Initializes th elanguage selectors with all BING supported languages</summary>
        /// <returns>Valuen indicating whether languages have been read.</returns>
        private bool InitializeLanguageSelectors()
        {
            // load languages
            var bingTranslationLanguageTags = this.GetBingLanguagesForTranslation();
            var bingCultures = new List<CultureInfo>();
            if (bingTranslationLanguageTags != null)
            {
                foreach (var tag in bingTranslationLanguageTags)
                {
                    try
                    {
                        switch (tag)
                        {
                                // "Hmong Daw" is the dialect of Hmong the system supports, also known as "White Hmong".
                                // Bing: mww, hm-HMN
                            case "mww":
                                bingCultures.Add(CultureInfo.GetCultureInfo("hmn"));
                                break;

                                // "Haitian Creole", Bing: ht                            
                            case "ht":
                                bingCultures.Add(CultureInfo.GetCultureInfo(tag));
                                break;

                            default:
                                bingCultures.Add(CultureInfo.GetCultureInfo(tag));
                                break;
                        }
                    }
                    catch (Exception ex)
                    {
                        var error = string.Format(CultureInfo.CurrentCulture, Resources.ErrorFormat, tag, ex.Message);
                        this.AddLogTextLine(error);
                    }
                }
            }

            AddLanguagesToComboBox(this.cmbBingFromCultureSingleFile, bingCultures);
            AddLanguagesToComboBox(this.cmbBingToCultureSingleFile, bingCultures);
            AddLanguagesToComboBox(this.cmbBingFromCultureDirectory, bingCultures);
            AddLanguagesToComboBox(this.cmbBingToCultureDirectory, bingCultures);

            return bingCultures.Count > 0;
        }

        /// <summary>The resx translator_ form_ load.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event args.</param>
        private void OnResxTranslatorFormLoad(object sender, EventArgs e)
        {
            // load app id from cache
            if (string.IsNullOrEmpty(this.AzureClientID))
            {
                try
                {
                    var sr = new StreamReader(Settings.Default.TranslatorClientCredentialsSettingsFileName);
                    try
                    {
                        this.AzureClientID = sr.ReadLine();
                        if (!sr.EndOfStream)
                        {
                            this.AzureClientSecret = sr.ReadLine();
                        }
                    }
                    finally
                    {
                        sr.Close();
                    }
                }
                catch (Exception)
                {
                    this.tabControl.SelectedTab = this.tabPageAdvanced;
                    MessageBox.Show(Resources.PleaseSetTranslatorCredentials);
                    return;
                }
            }

            this.startupTimer = new Timer { Interval = 1000 };
            this.startupTimer.Tick += delegate
                {
                    this.startupTimer.Enabled = false;
                    this.Cursor = Cursors.WaitCursor;
                    try
                    {
                        if (this.InitializeLanguageSelectors())
                        {
                           TranslatorUISingleFile.Enabled = true;
                           TranslatorUIDirectory.Enabled = true;
                        }
                        else
                        {
                            TranslatorUISingleFile.Enabled = false;
                            TranslatorUIDirectory.Enabled = false;
                            this.tabControl.SelectedTab = this.tabPageAdvanced;
                            MessageBox.Show(Resources.PleaseSetTranslatorCredentials);
                        }
                    }
                    finally
                    {
                        this.Cursor = Cursors.Default;
                    }
                };
            this.startupTimer.Start();
        }

        /// <summary>The btn browse from res x_ click.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event targs</param>
        private void OnBrowseSingleFileClick(object sender, EventArgs e)
        {
            var fileDialog = new OpenFileDialog
                {
                    Filter = Resources.ResxFileFilter, 
                    FilterIndex = 2, 
                    RestoreDirectory = true,                    
                };

            if (fileDialog.ShowDialog() == DialogResult.OK)
            {
                this.SourceResxFile = fileDialog.FileName;
            }

            this.TranslateNowSingleFile.Enabled = this.ReadyToTranslateSingleFile();
        }

        /// <summary>The btn browse for search dir.</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event targs</param>
        private void OnBrowseSearchRootDirClick(object sender, EventArgs e)
        {
            var dialog = new FolderBrowserDialog
                {
                    Description = Resources.SelectDirectoryDialog,
                    ShowNewFolderButton = false,
                    SelectedPath =
                        Directory.Exists(this.SourceSearchDirectory)
                            ? this.SourceSearchDirectory
                            : Directory.GetCurrentDirectory(),
                };

            if (dialog.ShowDialog() == DialogResult.OK)
            {
                this.SourceSearchDirectory = dialog.SelectedPath;
                this.textSearchRootDir.Text = this.SourceSearchDirectory;
            }

            this.TranslateNowDirectory.Enabled = this.ReadyToTranslateDirectory();
        }

        /// <summary>The ready to translate.</summary>
        /// <returns>Value indicating whether we aready to translate.</returns>
        private bool ReadyToTranslateSingleFile()
        {
            var ready = File.Exists(this.SourceResxFile);

            if (cmbBingFromCultureSingleFile.SelectedItem == null)
            {
                ready = false;
            }

            if (cmbTranslateToCultureSingleFile.SelectedItem == null)
            {
                ready = false;
            }

            return ready;
        }

        /// <summary>The ready to translate.</summary>
        /// <returns>Value indicating whether we aready to translate.</returns>
        private bool ReadyToTranslateDirectory()
        {
            var ready = Directory.Exists(this.SourceSearchDirectory);

            if (cmbBingFromCultureDirectory.SelectedItem == null)
            {
                ready = false;
            }

            if (cmbTranslateToCultureDirectory.SelectedItem == null)
            {
                ready = false;
            }

            return ready;
        }

        /// <summary>The cmd from_ selected index changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void FromBingCultureSingleFileSelectedIndexChanged(object sender, EventArgs e)
        {
            this.TranslateNowSingleFile.Enabled = this.ReadyToTranslateSingleFile();
        }

        /// <summary>The cmd from_ selected index changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void FromBingCultureDirectorySelectedIndexChanged(object sender, EventArgs e)
        {
            var fromCulture = cmbBingFromCultureDirectory.SelectedItem as CultureInfo;
            InitTranslateToCultureComboBox(fromCulture, this.cmbTranslateFromCultureDirectory);
            this.TranslateNowDirectory.Enabled = this.ReadyToTranslateDirectory();
        }

        /// <summary>
        /// UI event handler
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnTranslateFromCultureDirectorySelectedIndexChanged(object sender, EventArgs e)
        {
            var comboCultureItem = this.cmbTranslateFromCultureDirectory.SelectedItem as CultureComboItem;
            if ((comboCultureItem == null) || (comboCultureItem.Culture == null))
            {
                this.textSearchFilter.Text = "*.resx";
            }
            else
            {
                this.textSearchFilter.Text = "*." + comboCultureItem.Culture.Name + ".resx";
            }
        }

        /// <summary>The cmd to_ selected index changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void ToBingCultureSingleFileSelectedIndexChanged(object sender, EventArgs e)
        {
            var toCulture = cmbBingToCultureSingleFile.SelectedItem as CultureInfo;
            InitTranslateToCultureComboBox(toCulture, this.cmbTranslateToCultureSingleFile);
            this.TranslateNowSingleFile.Enabled = this.ReadyToTranslateSingleFile();
        }

        /// <summary>The cmd to_ selected index changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void ToBingCultureDirectorySelectedIndexChanged(object sender, EventArgs e)
        {
            var toCulture = cmbBingToCultureDirectory.SelectedItem as CultureInfo;
            InitTranslateToCultureComboBox(toCulture, this.cmbTranslateToCultureDirectory);
            this.TranslateNowDirectory.Enabled = this.ReadyToTranslateDirectory();
        }

        /// <summary>The txtfrom resx path_ text changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void FromResxPathTextChanged(object sender, EventArgs e)
        {
            this.TranslateNowSingleFile.Enabled = this.ReadyToTranslateSingleFile();
        }

        /// <summary>The txtfrom resx path_ text changed.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void FromSearchRootDirTextChanged(object sender, EventArgs e)
        {
            this.SourceSearchDirectory = this.textSearchRootDir.Text;
            this.TranslateNowDirectory.Enabled = this.ReadyToTranslateDirectory();
        }

        /// <summary>The btn save app i d_ click.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event args.</param>
        private void SaveAppIdButtonClick(object sender, EventArgs e)
        {
            this.ClearLog();
            if (this.InitializeLanguageSelectors())
            {
                TranslatorUISingleFile.Enabled = true;
                TranslatorUIDirectory.Enabled = true;
                var sw = new StreamWriter(Settings.Default.TranslatorClientCredentialsSettingsFileName);
                sw.WriteLine(this.AzureClientID);
                sw.WriteLine(this.AzureClientSecret);
                sw.Close();
            }
            else
            {
                TranslatorUISingleFile.Enabled = false;
                TranslatorUIDirectory.Enabled = false;
                this.tabControl.SelectedTab = this.tabPageAdvanced;
                MessageBox.Show(Resources.PleaseSetTranslatorCredentials);
            }
            
            this.TranslateNowSingleFile.Enabled = this.ReadyToTranslateSingleFile();
        }       

        /// <summary>Azure link has been clicked</summary>
        /// <param name="sender">The event sender.</param>
        /// <param name="e">The event arguments</param>
        private void OnAzureLinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start(TranslatorTools.AzureMarketTranslatorLandingPage);
        }

        /// <summary>The Stop button has been clicked.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private void OnStopButtonClick(object sender, EventArgs e)
        {
            this.translationAbortRequested = true;
        }

        /// <summary>The translate now button click.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private void TranslateNowButtonSingleFileClick(object sender, EventArgs e)
        {
            this.ClearLog();
            Application.DoEvents();

            // Set status
            TranslatorUISingleFile.Enabled = false;
            TranslatorUIDirectory.Enabled = false;
            Cursor.Current = Cursors.WaitCursor;
            var translationResult = new TranslationResult();
            try
            {
                this.StopButton.Enabled = true;
                this.translationAbortRequested = false;

                // get the source RESX(s)
                if (!string.IsNullOrEmpty(this.SourceResxFile))
                {
                    var authenticationToken = TranslatorTools.RequestAuthenticationToken(
                        this.AzureClientID, this.AzureClientSecret, this.AddLogText);

                    if (authenticationToken != null)
                    {
                        var toFileCulture =
                            ((CultureComboItem)this.cmbTranslateToCultureSingleFile.SelectedItem).Culture;
                        translationResult += TranslatorTools.TranslateResXFile(
                            this.SourceResxFile,
                            (CultureInfo)this.cmbBingFromCultureSingleFile.SelectedItem,
                            (CultureInfo)this.cmbBingToCultureSingleFile.SelectedItem,
                            toFileCulture,
                            authenticationToken,
                            this.AddLogText,
                            () => this.translationAbortRequested);
                    }
                    else
                    {
                        this.translationAbortRequested = true;
                    }

                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.TranslationsProcessed,
                        translationResult.FileCount,
                        translationResult.StringCount,
                        translationResult.CharacterCount);
                    if (this.translationAbortRequested)
                    {
                        message += Resources.ErrorsFound;
                        this.tabPageAdvanced.Select();
                    }

                    this.AddLogTextLine(message);
                }
            }
            finally
            {
                // reset status
                this.StopButton.Enabled = false;
                TranslatorUISingleFile.Enabled = true;
                TranslatorUIDirectory.Enabled = true;
                Cursor.Current = Cursors.Default;
            }

            var finalMessage = Resources.LogTranslationCompleted + "\r\n"
                               +
                               string.Format(
                                   CultureInfo.CurrentCulture,
                                   Resources.TranslationsProcessed,
                                   translationResult.FileCount,
                                   translationResult.StringCount,
                                   translationResult.CharacterCount);

            if (this.translationAbortRequested)
            {
                finalMessage += "\r\n" + Resources.ErrorsFound;
            }

            TranslatorTools.LogStatus(finalMessage, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>The translate now button click.</summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The event arguments.</param>
        private void TranslateNowDirectoryClick(object sender, EventArgs e)
        {
            this.ClearLog();
            Application.DoEvents();

            // Set status
            TranslatorUISingleFile.Enabled = false;
            TranslatorUIDirectory.Enabled = false;
            Cursor.Current = Cursors.WaitCursor;
            var translationResult = new TranslationResult();
            try
            {
                this.StopButton.Enabled = true;
                this.translationAbortRequested = false;

                // get the source RESX(s)
                if (!string.IsNullOrEmpty(this.SourceSearchDirectory))
                {
                    var authenticationToken = TranslatorTools.RequestAuthenticationToken(
                        this.AzureClientID, this.AzureClientSecret, this.AddLogText);

                    if (authenticationToken != null)
                    {
                        var toFileCulture = ((CultureComboItem)this.cmbTranslateToCultureDirectory.SelectedItem).Culture;
                        translationResult += TranslatorTools.TranslateResXDirectory(
                            this.SourceSearchDirectory,
                            this.textSearchFilter.Text,
                            (CultureInfo)this.cmbBingFromCultureDirectory.SelectedItem,
                            (CultureInfo)this.cmbBingToCultureDirectory.SelectedItem,
                            toFileCulture,
                            authenticationToken,
                            this.AddLogText,
                            () => this.translationAbortRequested);
                    }
                    else
                    {
                        this.translationAbortRequested = true;
                    }

                    var message = string.Format(
                        CultureInfo.CurrentCulture,
                        Resources.TranslationsProcessed,
                        translationResult.FileCount,
                        translationResult.StringCount,
                        translationResult.CharacterCount);
                    if (this.translationAbortRequested)
                    {
                        message += Resources.ErrorsFound;
                        this.tabPageAdvanced.Select();
                    }

                    this.AddLogTextLine(message);
                }
            }
            finally
            {
                // reset status
                this.StopButton.Enabled = false;
                TranslatorUISingleFile.Enabled = true;
                TranslatorUIDirectory.Enabled = true;
                Cursor.Current = Cursors.Default;
            }

            var finalMessage = Resources.LogTranslationCompleted + "\r\n"
                               +
                               string.Format(
                                   CultureInfo.CurrentCulture,
                                   Resources.TranslationsProcessed,
                                   translationResult.FileCount,
                                   translationResult.StringCount,
                                   translationResult.CharacterCount);

            if (this.translationAbortRequested)
            {
                finalMessage += "\r\n" + Resources.ErrorsFound;
            }

            TranslatorTools.LogStatus(finalMessage, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>
        /// add text to log text box
        /// </summary>
        /// <param name="message">text to log</param>
        private void AddLogText(string message)
        {
            if (message.Contains("Error:"))
            {
                this.translationAbortRequested = true;
            }

            if (message.EndsWith("\r\n"))
            {
                this.AddLogTextInternal(message);
                this.SetStatusText(this.statusLineText + message.Substring(0, message.Length - 2));
                this.statusLineText = string.Empty;
            }
            else
            {
                this.AddLogTextInternal(message);
                this.statusLineText += message;
                this.SetStatusText(this.statusLineText);
            }
        }

        /// <summary>
        /// add text to log text box
        /// </summary>
        /// <param name="message">text to log</param>
        private void AddLogTextInternal(string message)
        {
            this.errorLog.Text += message;
            this.errorLog.ScrollToCaret();
            this.errorLog.SelectionStart = this.errorLog.Text.Length;
            this.errorLog.Refresh();
        }

        /// <summary>
        /// add text line to log text box
        /// </summary>
        /// <param name="message">text to log</param>
        private void AddLogTextLine(string message)
        {
            this.AddLogTextInternal(message + "\r\n");
            this.SetStatusText(message);
            this.statusLineText = message;
        }

        /// <summary>
        /// Clear th elog
        /// </summary>
        private void ClearLog()
        {
            this.errorLog.Clear();
            this.ClearStatusText();
        }

        /// <summary>
        /// Sets the status message
        /// </summary>
        /// <param name="message">The message</param>
        private void SetStatusText(string message)
        {
            this.toolStripLogStatusText.Text = message;
            this.statusStrip.Update();
        }

        /// <summary>
        /// Clears the status text.
        /// </summary>
        private void ClearStatusText()
        {
            this.statusLineText = string.Empty;
            this.toolStripLogStatusText.Text = string.Empty;
            this.statusStrip.Update();
        }
    }
}